import os
import time
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import List, Dict, Any, Optional, Callable

logger = logging.getLogger(__name__)


class ParallelScanner:
    """
    并行文件扫描器，使用多线程扫描文件系统
    """
    def __init__(self, max_workers: int = None, scan_depth: int = -1):
        """
        初始化并行扫描器
        :param max_workers: 最大工作线程数，如果为None则使用默认值
        :param scan_depth: 扫描深度，-1表示无限制
        """
        self.max_workers = max_workers or os.cpu_count() or 4
        self.scan_depth = scan_depth
        
    def _scan_directory_recursive(self, directory: str, current_depth: int, 
                                progress_callback: Optional[Callable[[int], None]] = None, 
                                stop_event: Optional[Callable[[], bool]] = None) -> List[Dict[str, Any]]:
        """
        递归扫描目录
        :param directory: 要扫描的目录
        :param current_depth: 当前深度
        :param progress_callback: 进度回调函数
        :param stop_event: 停止事件函数
        :return: 文件信息列表
        """
        files_info = []
        
        # 检查是否需要停止
        if stop_event and stop_event():
            logger.info('扫描操作已停止')
            return []
            
        # 检查是否达到最大扫描深度
        if self.scan_depth != -1 and current_depth > self.scan_depth:
            return []
            
        try:
            # 列出目录内容
            with os.scandir(directory) as entries:
                for entry in entries:
                    # 检查是否需要停止
                    if stop_event and stop_event():
                        logger.info('扫描操作已停止')
                        return []
                        
                    try:
                        if entry.is_file(follow_symlinks=False):
                            # 获取文件信息
                            file_stat = entry.stat(follow_symlinks=False)
                            # 确保路径使用Windows标准格式
                            normalized_path = os.path.normpath(entry.path)
                            file_info = {
                                'path': normalized_path,
                                'size': file_stat.st_size,
                                'modified_time': file_stat.st_mtime,
                                'created_time': file_stat.st_ctime if hasattr(file_stat, 'st_ctime') else file_stat.st_mtime,
                                'accessed_time': file_stat.st_atime if hasattr(file_stat, 'st_atime') else file_stat.st_mtime,
                                'is_directory': False
                            }
                            files_info.append(file_info)
                            
                            # 调用进度回调
                            if progress_callback:
                                progress_callback(1)
                                 
                        elif entry.is_dir(follow_symlinks=False):
                            # 递归扫描子目录
                            # 确保路径使用Windows标准格式
                            normalized_subdir_path = os.path.normpath(entry.path)
                            subdir_files = self._scan_directory_recursive(
                                normalized_subdir_path, current_depth + 1, progress_callback, stop_event
                            )
                            files_info.extend(subdir_files)
                    except PermissionError:
                        logger.warning(f'没有权限访问: {os.path.normpath(entry.path)}')
                    except Exception as e:
                        logger.error(f'扫描文件时出错: {os.path.normpath(entry.path)}, 错误: {str(e)}')
        except PermissionError:
            logger.warning(f'没有权限访问目录: {directory}')
        except Exception as e:
            logger.error(f'扫描目录时出错: {directory}, 错误: {str(e)}')
            
        return files_info
        
    def scan_directory(self, directory: str, progress_callback: Optional[Callable[[int, int], None]] = None, 
                      stop_event: Optional[Callable[[], bool]] = None) -> List[Dict[str, Any]]:
        """
        并行扫描目录
        :param directory: 要扫描的目录
        :param progress_callback: 进度回调函数，接收已扫描文件数和总文件估计数
        :param stop_event: 停止事件函数，返回True表示需要停止处理
        :return: 文件信息列表
        """
        if not os.path.isdir(directory):
            logger.error(f'无效的目录路径: {directory}')
            return []
            
        logger.info(f'开始并行扫描目录: {directory}，线程数: {self.max_workers}')
        start_time = time.time()
        
        # 首先快速扫描获取所有子目录，用于并行处理
        all_directories = [directory]
        try:
            with os.scandir(directory) as entries:
                for entry in entries:
                    if entry.is_dir(follow_symlinks=False):
                        normalized_path = os.path.normpath(entry.path)
                        all_directories.append(normalized_path)
                        # 如果有嵌套子目录，也加入列表以增加并行度
                        if self.scan_depth == -1 or self.scan_depth > 1:
                            try:
                                with os.scandir(normalized_path) as sub_entries:
                                    for sub_entry in sub_entries:
                                        if sub_entry.is_dir(follow_symlinks=False):
                                            sub_normalized_path = os.path.normpath(sub_entry.path)
                                            all_directories.append(sub_normalized_path)
                            except Exception:
                                pass
        except Exception as e:
            logger.error(f'获取子目录列表时出错: {str(e)}')
            
        total_directories = len(all_directories)
        processed_directories = 0
        files_info = []
        
        # 使用线程池并行扫描
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交所有扫描任务
            future_to_dir = {
                executor.submit(self._scan_directory_recursive, dir_path, 0, 
                               lambda _: None, stop_event): dir_path
                for dir_path in all_directories
            }
            
            # 处理完成的任务
            for future in as_completed(future_to_dir):
                dir_path = future_to_dir[future]
                try:
                    # 获取扫描结果
                    dir_files = future.result()
                    files_info.extend(dir_files)
                    
                    # 更新进度
                    processed_directories += 1
                    if progress_callback:
                        # 这里我们只能提供已处理目录数作为进度参考
                        progress_callback(processed_directories, total_directories)
                except Exception as e:
                    logger.error(f'扫描目录时出错: {dir_path}, 错误: {str(e)}')
                
                # 检查是否需要停止
                if stop_event and stop_event():
                    logger.info('扫描操作已停止')
                    executor.shutdown(wait=False, cancel_futures=True)
                    break
        
        end_time = time.time()
        logger.info(f'目录扫描完成，共扫描 {len(files_info)} 个文件，耗时: {end_time - start_time:.2f} 秒')
        
        return files_info